﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using System.ComponentModel.DataAnnotations;
using projectManagementWeb.Models;
using projectManagementWeb.Models.ProjectLeader;
using System.Data.Linq;
using System.ComponentModel;
using System.Collections;
using System.Web.Security;
using System.Diagnostics;

namespace projectManagementWeb.Validations
{


    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple= false, Inherited= true)]
    public class UserValidationAttribute: ValidationAttribute 
    {
        private string rolValue = "-1";

        public UserValidationAttribute() : base(){}

        public override bool IsValid(object value)
        {
            string rolDescValue = (string)value;
            if (rolDescValue == rolValue)
            {
                base.ErrorMessage = "El rol es un campo requerido, eliga una opcion";
                return false;
            }
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class QuestionValidationAttribute : ValidationAttribute
    {
        private string questionValue = "-1";
        public QuestionValidationAttribute() : base(){}
        public override bool IsValid(object value)
        {
            string questionDescValue = (string)value;
            if (questionDescValue == questionValue)
            {
                base.ErrorMessage = "La pregunta es un campo requerido, eliga una opcion";
                return false;
            }
            return true;
        }
    }


    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple= false, Inherited= true)]
    public class UserNameValidationAttribute: ValidationAttribute
    {
        private string username = "admin";
        public UserNameValidationAttribute() :  base() {}
        public override bool IsValid(object value)
        {
            string name = (string)value;
            if (name == username)
            {
                base.ErrorMessage = "El nombre de usuario no puede ser 'admin'";
                return false;
            }
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class moduleIDValidationAttribute: ValidationAttribute
    {
        projectmanagementDBDataContext _dbContext = new projectmanagementDBDataContext();
        public moduleIDValidationAttribute() : base() { }
        public override bool IsValid(object value)
        {
            int id = (int)value;
            if (!_dbContext.getAllModules().Select(x => x.ID).Contains(id))
            {
                base.ErrorMessage = "El Modulo seleccionado no es valido";
                return false;
            }
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class IDValidationAttribute : ValidationAttribute
    {
        public IDValidationAttribute() : base() { }
        public override bool IsValid(object value)
        {
            int id = (int)value;
            if (id < 1)
            {
                base.ErrorMessage = "El ID debe de ser un numero mayor a 0.";
                return false;
            }
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class TaskListValidationAttribute : ValidationAttribute
    {
        public TaskListValidationAttribute() : base(){}
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                base.ErrorMessage = "No hay tareas asignadas que guardar";
                return false;
            }
            projectmanagementDBDataContext _dbContext = new projectmanagementDBDataContext();
            List<TaskDescriptionModel> taskList = (List<TaskDescriptionModel>)value;

            //Get the status flags for the Developer
            ISingleResult<spGetDeveloperStatusFlagsResult> statusList = _dbContext.spGetDeveloperStatusFlags();
            //Get the ID for the completion status
            int completionStatusID = 0;
            int notStartedStatusID = 0;

            foreach (spGetDeveloperStatusFlagsResult status in statusList)
            {
                if (status.name == "Completado")
                {
                    completionStatusID = status.ID;
                }
                if (status.name == "No Iniciado")
                {
                    notStartedStatusID = status.ID;
                }
            }

            //Validate for the completed tasks
            foreach (TaskDescriptionModel task in taskList)
            {
                if (task.CurrentEffortPending < 0 || task.CurrentEffortDone < 0)
                {
                    base.ErrorMessage = "Solo se aceptan valores positivos";
                    return false;
                }
                if (task.CurrentStatus == completionStatusID)
                {
                    if (task.CurrentEffortPending > 0)
                    {
                        base.ErrorMessage = "Las tareas completadas no pueden tener esfuerzo pendiente";
                        return false;
                    }
                }

                if (task.CurrentStatus == notStartedStatusID)
                {
                    if (task.CurrentEffortPending != task.PlannedEstimate || task.CurrentEffortDone > 0)
                    {
                        base.ErrorMessage = "Las tareas no iniciadas no deben de tener esfuerzo y esfuerzo faltante debe coincidir con el planeado";
                        return false;
                    }
                }
            }
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class TaskDescriptionClassValidationAttribute : ValidationAttribute
    {
        public TaskDescriptionClassValidationAttribute() : base() { }
        public override bool IsValid(object value)
        {
            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
                string TaskName = (string)properties.Find("TaskName", true /* ignoreCase */).GetValue(value);
                string TaskDescription = (string)properties.Find("TaskDescription", true /* ignoreCase */).GetValue(value);
                int PlannedEstimate = (int)properties.Find("PlannedEstimate", true /* ignoreCase */).GetValue(value);
                int ModuleID = (int)properties.Find("ModuleID", true /* ignoreCase */).GetValue(value);
                int ProjectID = (int)properties.Find("ProjectID", true /* ignoreCase */).GetValue(value);
                DateTime StartDate = (DateTime)properties.Find("StartDate", true /* ignoreCase */).GetValue(value);
                DateTime EndDate = (DateTime)properties.Find("EndDate", true /* ignoreCase */).GetValue(value);
                bool error = false;
                if (EndDate == null)
                {
                    base.ErrorMessage = "La fecha de finalización es requerida";
                    error = true;
                }
                if (StartDate == null)
                {
                    base.ErrorMessage = "La fecha de inicio es requerida";
                    error = true;
                }
                if (PlannedEstimate <= 0)
                {
                    base.ErrorMessage = "El valor planeado no puede ser menor o igual a cero";
                    error = true;
                }
               /* if (PlannedEstimate == null)
                {
                    base.ErrorMessage = "El valor planeado es requerido";
                    error = true;
                }*/

                if (TaskDescription == null)
                {
                    base.ErrorMessage = "La descripción de la tarea es requerida";
                    error = true;
                }
                if (TaskName == null)
                {
                    base.ErrorMessage = "El nombre de la tarea es requerido";
                    error = true;
                }


                if (error == true) {
                    return false;
                }

                try
                {
                    projectmanagementDBDataContext _dbContext = new projectmanagementDBDataContext();
                    string ModuleName = _dbContext.spGetModuleName(ModuleID).First().name;

                    if (ModuleName == "Ninguno")
                    {
                        ISingleResult<spGetProjectByIDResult> projectData = _dbContext.spGetProjectByID(ProjectID);
                        ISingleResult<spGetProjectByIDResult> projectData2 = _dbContext.spGetProjectByID(ProjectID);
                        DateTime projectStartDate = (DateTime)projectData.First().startDate;
                        DateTime projectEndDate = (DateTime)projectData2.First().endDate;

                        //First check consistency between the start and end dates
                        if (StartDate.Date > EndDate.Date)
                        {
                            base.ErrorMessage = "La fecha de inicio no puede ser después de la fecha de finalización";
                            return false;
                        }

                        if (projectStartDate != null && projectStartDate.Date > StartDate.Date)
                        {
                            base.ErrorMessage = "La tarea no puede comenzar antes que el proyecto al que pertenece: " + projectStartDate.ToShortDateString();
                            return false;
                        }

                        if (projectEndDate != null && projectEndDate.Date < EndDate.Date)
                        {
                            base.ErrorMessage = "La tarea no puede terminar después del proyecto al que pertenece: " + projectEndDate.ToShortDateString();
                            return false;
                        }
                    }
                    else
                    {
                        ISingleResult<spGetModuleByIDResult> moduleData = _dbContext.spGetModuleByID(ModuleID);
                        ISingleResult<spGetModuleByIDResult> moduleData2 = _dbContext.spGetModuleByID(ModuleID);
                        DateTime moduleStartDate = (DateTime)moduleData.First().startDate;
                        DateTime moduleEndDate = (DateTime)moduleData2.First().endDate;

                        if (StartDate.Date > EndDate.Date)
                        {
                            base.ErrorMessage = "La fecha de inicio no puede ser después de la fecha de finalización";
                            return false;
                        }

                        if (moduleStartDate != null && moduleStartDate.Date > StartDate.Date)
                        {
                            base.ErrorMessage = "La tarea no puede comenzar antes que el módulo al que pertenece: " + moduleStartDate.ToShortDateString();
                            return false;
                        }

                        if (moduleEndDate != null && moduleEndDate.Date < EndDate.Date)
                        {
                            base.ErrorMessage = "La tarea no puede terminar después del módulo al que pertenece: " + moduleEndDate.ToShortDateString();
                            return false;
                        }

                    }
                }
                catch (Exception)
                {
                    base.ErrorMessage = "Ocurrio un error en la informacion, por favor intentelo de nuevo.";
                    return false;
                }

            }
            catch (Exception)
            {
                base.ErrorMessage = "Error al leer los datos de la tarea";
                return false;
            }

            return true;

        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class  ProjectLeaderTaskListValidationAttribute : ValidationAttribute
    {
        public ProjectLeaderTaskListValidationAttribute() : base() { }
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                base.ErrorMessage = "No hay tareas en el actual proyecto";
                return false;
            }
            projectmanagementDBDataContext _dbContext = new projectmanagementDBDataContext();
            List<ProjectLeadTaskDescriptionModel> taskList = (List<ProjectLeadTaskDescriptionModel>)value;

            //Get the status flags for the Developer
            ISingleResult<spGetDeveloperStatusFlagsResult> statusList = _dbContext.spGetDeveloperStatusFlags();
            //Get the ID for the completion status
            int completionStatusID = 0;
            int notStartedStatusID = 0;

            foreach (spGetDeveloperStatusFlagsResult status in statusList)
            {
                if (status.name == "Completado")
                {
                    completionStatusID = status.ID;
                }
                if (status.name == "No Iniciado")
                {
                    notStartedStatusID = status.ID;
                }
            }

            //Validate for the completed tasks
            foreach (ProjectLeadTaskDescriptionModel task in taskList)
            {
                if (task.PlannedEstimate < 1)
                {
                    base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ": El valor planeado no puede ser 0";
                    return false;
                }
                if (task.CurrentEffortPending < 0 || task.CurrentEffortDone < 0)
                {
                    base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ":Solo se aceptan valores positivos";
                    return false;
                }
                if (task.CurrentStatus == completionStatusID)
                {
                    if (task.CurrentEffortPending > 0)
                    {
                        base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ":Las tareas completadas no pueden tener esfuerzo pendiente";
                        return false;
                    }
                }

                if (task.CurrentStatus == notStartedStatusID)
                {
                    if (task.CurrentEffortPending != task.PlannedEstimate || task.CurrentEffortDone > 0)
                    {
                        base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ":Las tareas no iniciadas no deben de tener esfuerzo y esfuerzo faltante debe coincidir con el planeado";
                        return false;
                    }
                }

                //Validation for the dates
                //Validate against the module if any
                if (task.ModuleID != int.Parse(ProjectLeadTaskListModel.DUMMY_ID))
                {
                    //Project after that
                    ISingleResult<spGetModuleByIDResult> moduleList = _dbContext.spGetModuleByID((int?)task.ModuleID);
                    Nullable<DateTime> moduleStartDate = new DateTime();
                    Nullable<DateTime> moduleEndDate = new DateTime();

                    foreach (spGetModuleByIDResult result in moduleList)
                    {
                        moduleStartDate = result.startDate;
                        moduleEndDate = result.endDate;
                    }

                    if (moduleStartDate != null && moduleStartDate > task.StartDate)
                    {
                        base.ErrorMessage = "Tarea ID"+task.TaskID.ToString() + ":Las tareas no pueden comenzar antes que el módulo al que pertenecen:"+moduleStartDate.Value.ToShortDateString();
                        return false;
                    }

                    if (moduleEndDate != null && moduleEndDate < task.EndDate)
                    {
                        base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ":Las tareas no pueden terminar después del módulo al que pertenecen:" + moduleEndDate.Value.ToShortDateString();
                        return false;
                    }
 
                }

                //Project after that
                ISingleResult<spGetProjectByIDResult> projectList = _dbContext.spGetProjectByID(task.ProjectID);
                Nullable<DateTime> projectStartDate = new DateTime();
                Nullable<DateTime> projectEndDate = new DateTime();

                foreach (spGetProjectByIDResult result in projectList)
                {
                    projectStartDate = result.startDate;
                    projectEndDate = result.endDate;
                }

                //First check consistency between the start and end dates
                if (task.StartDate.Date > task.EndDate.Date)
                {
                    base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ":La fecha de inicio no puede ser después de la fecha de finalización";
                    return false;
                }

                if (projectStartDate != null && projectStartDate.Value.Date > task.StartDate.Date)
                {
                    base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ":Las tareas no pueden comenzar antes que el proyecto al que pertenecen:" + projectStartDate.Value.ToShortDateString();
                    return false;
                }

                if (projectEndDate != null && projectEndDate.Value.Date < task.EndDate.Date)
                {
                    base.ErrorMessage = "Tarea ID" + task.TaskID.ToString() + ":Las tareas no pueden terminar después del proyecto al que pertenecen:"+ projectEndDate.Value.ToShortDateString();
                    return false;
                }

            }
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class ModuleClassValidationAttribute : ValidationAttribute
    {
        public ModuleClassValidationAttribute() : base()
        {

        }
        public override bool IsValid(object value)
        {
            try
            {

                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
                int projectID = (int)properties.Find("projectID", true /* ignoreCase */).GetValue(value);
                if (projectID < 1)
                {
                    base.ErrorMessage = "Debe de seleccionar un Proyecto valido";
                    return false;
                }
                DateTime startDate = (DateTime)properties.Find("startDate", true /* ignoreCase */).GetValue(value);
                DateTime endDate = (DateTime)properties.Find("endDate", true /* ignoreCase */).GetValue(value);
                try
                {
                    projectmanagementDBDataContext dbContext = new projectmanagementDBDataContext();

                    

                    var projectData = dbContext.spGetProjectByID(projectID).First();
                    if (startDate.Date > endDate.Date)
                    {
                        base.ErrorMessage = "La fecha de inicio debe de ser menor o igual a la fecha finalizacion.";
                        return false;
                    }
                    if (startDate.Date < projectData.startDate.Value.Date)
                    {
                        base.ErrorMessage = "La fecha de inicio del modulo debe de ser mayor o igual a la fecha de inicio del proyecto (" + projectData.startDate.Value.Date + ").";
                        return false;
                    }
                    if (endDate.Date > projectData.endDate.Value.Date)
                    {
                        base.ErrorMessage = "La fecha de finalizacion del modulo debe de ser menor o igual a la fecha de finalizacion del proyecto (" + projectData.endDate.Value.Date + ").";
                        return false;
                    }

                    //Validación contra las tareas del módulo
                    try
                    {

                        //Extraemos las tareas del módulo
                        int ModuleID = (int)properties.Find("ID", true).GetValue(value);
                        ISingleResult<spGetModuleTasksResult> tasks = dbContext.spGetModuleTasks(ModuleID);
                        DateTime minStartDate = DateTime.MaxValue;
                        DateTime maxEndDate = DateTime.MinValue;

                        foreach (spGetModuleTasksResult task in tasks)
                        {
                            if (task.endDate >= maxEndDate)
                                maxEndDate = task.endDate;
                            if (task.startDate <= minStartDate)
                                minStartDate = task.startDate;
                        }

                        //Realizamos la validación 
                        if (startDate > minStartDate)
                        {
                            base.ErrorMessage = "La fecha de inicializacion del modulo debe de ser menor o igual a la fecha de inicializacion de las tareas del modulo.";
                            return false;
                        }

                        if (endDate < maxEndDate)
                        {
                            base.ErrorMessage = "La fecha de finalizacion del modulo debe de ser mayor o igual a la fecha de finalizacion de las tareas del modulo";
                            return false;
                        }

                        if (endDate < startDate)
                        {
                            base.ErrorMessage = "La fecha de finalizacion no puede ser menor que  la fecha de inicializacion";
                            return false;
                        }

                    }
                    catch (Exception)
                    {
                        //nothing, it means we are creating a module
                    }
                    
                }
                catch (Exception)
                {
                    base.ErrorMessage = "Ocurrio un error en la informacion, por favor intentelo de nuevo.";
                    return false;
                }
            }
            catch (Exception)
            {
                base.ErrorMessage = "Seleccione un modulo valido y vuelva a intentarlo";
                return false;
            }
            
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class ProjectClassValidationAttribute : ValidationAttribute
    {
        public ProjectClassValidationAttribute()
            : base()
        {

        }
        public override bool IsValid(object value)
        {
            try
            {
                projectmanagementDBDataContext _dbContext = new projectmanagementDBDataContext();
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
                int projectID = (int)properties.Find("ID", true /* ignoreCase */).GetValue(value);
                if (projectID < 1)
                {
                    base.ErrorMessage = "Debe de seleccionar un Proyecto valido";
                    return false;
                }
                int projectLeaderID = (int)properties.Find("projectLeaderID", true /* ignoreCase */).GetValue(value);
                if (projectLeaderID < 1)
                {
                    base.ErrorMessage = "Debe de seleccionar un Lider Proyecto valido";
                    return false;
                }

                try
                {
                    DateTime startDate = (DateTime)properties.Find("startDate", true /* ignoreCase */).GetValue(value);
                    DateTime endDate = (DateTime)properties.Find("endDate", true /* ignoreCase */).GetValue(value);

                    if (startDate.Date > endDate.Date)
                    {
                        base.ErrorMessage = "La fecha de inicio debe de ser menor o igual a la fecha finalizacion.";
                        return false;
                    }
                }
                catch (Exception) { }

                try
                {
                    List<SelectListItem> projectLeaderIDList = _dbContext.spGetProyectLeaders()
                    .Select(i => new SelectListItem
                    {
                        Value = i.id.ToString(),
                        Text = i.UserName.ToString()
                    }).ToList();



                    if (!(projectLeaderIDList.Select(x => x.Value)).Contains(projectLeaderID.ToString()))
                    {
                        base.ErrorMessage = "Debe de seleccionar un Lider de Proyecto valido";
                        return false;
                    }

                    List<SelectListItem> projectLeaderIDListTMP = _dbContext.spGetAvailableProjectLeaders()
                    .Select(i => new SelectListItem
                    {
                        Value = i.id.ToString(),
                        Text = i.UserName.ToString()
                    }).ToList();
                    ISingleResult<spGetProjectByIDResult> pro = _dbContext.spGetProjectByID(projectID);
                    ISingleResult<spGetUserByIDResult> user = _dbContext.spGetUserByID(pro.First().projectLeaderID);
                    spGetUserByIDResult firstuser = user.First();
                    projectLeaderIDListTMP.Add(new SelectListItem
                    {
                        Value = firstuser.id.ToString(),
                        Text = firstuser.UserName.ToString()
                    });

                    if (!(projectLeaderIDListTMP.Select(x => x.Value)).Contains(projectLeaderID.ToString()))
                    {
                        base.ErrorMessage = "Debe de seleccionar un Lider de Proyecto valido (No asignado a otro proyecto)";
                        return false;
                    }

                }
                catch (Exception)
                {
                    base.ErrorMessage = "Ocurrio un error en la informacion, por favor intentelo de nuevo.";
                    return false;
                }
            }
            catch (Exception)
            {
                base.ErrorMessage = "Seleccione un Proyecto valido y vuelva a intentarlo";
                return false;
            }

            return true;
        }
    }


    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class CreateProjectClassValidationAttribute : ValidationAttribute
    {
        public CreateProjectClassValidationAttribute()
            : base()
        {

        }
        public override bool IsValid(object value)
        {
            try
            {
                projectmanagementDBDataContext _dbContext = new projectmanagementDBDataContext();
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);

                int projectLeaderID = (int)properties.Find("projectLeaderID", true /* ignoreCase */).GetValue(value);
                if (projectLeaderID < 1)
                {
                    base.ErrorMessage = "Debe de seleccionar un Lider Proyecto valido";
                    return false;
                }

                try
                {
                    DateTime startDate = (DateTime)properties.Find("startDate", true /* ignoreCase */).GetValue(value);
                    DateTime endDate = (DateTime)properties.Find("endDate", true /* ignoreCase */).GetValue(value);

                    if (startDate.Date > endDate.Date)
                    {
                        base.ErrorMessage = "La fecha de inicio debe de ser menor o igual a la fecha finalizacion.";
                        return false;
                    }
                }
                catch (Exception) { }

                try
                {
                    List<SelectListItem> projectLeaderIDList = _dbContext.spGetProyectLeaders()
                    .Select(i => new SelectListItem
                    {
                        Value = i.id.ToString(),
                        Text = i.UserName.ToString()
                    }).ToList();



                    if (!(projectLeaderIDList.Select(x => x.Value)).Contains(projectLeaderID.ToString()))
                    {
                        base.ErrorMessage = "Debe de seleccionar un Lider de Proyecto valido";
                        return false;
                    }

                    List<SelectListItem> projectLeaderIDListTMP = _dbContext.spGetAvailableProjectLeaders()
                    .Select(i => new SelectListItem
                    {
                        Value = i.id.ToString(),
                        Text = i.UserName.ToString()
                    }).ToList();
                    

                    if (!(projectLeaderIDListTMP.Select(x => x.Value)).Contains(projectLeaderID.ToString()))
                    {
                        base.ErrorMessage = "Debe de seleccionar un Lider de Proyecto valido (No asignado a otro proyecto)";
                        return false;
                    }

                }
                catch (Exception)
                {
                    base.ErrorMessage = "Ocurrio un error en la informacion, por favor intentelo de nuevo.";
                    return false;
                }
            }
            catch (Exception)
            {
                base.ErrorMessage = "Seleccione un Proyecto valido y vuelva a intentarlo";
                return false;
            }

            return true;
        }
    }

}